#include "unitest.h"
#include "single_thread_scheduler.h"

class SingleThreadSchedulerUnitest : public testing::Test {
protected:
    void SetUp() override {
    }

    void TearDown() override {
    }
};

TEST_F(SingleThreadSchedulerUnitest, name_priority) {
    std::string expect_name{"scheduler"};
#if defined(QNX)
    int32_t expect_priority{100};
#elif defined(LINUX)
    int32_t expect_priority{0};
#endif
    auto scheduler = std::make_unique<SingleThreadScheduler>(expect_priority, expect_name);
    scheduler->init();

    char real_name[32];
    pthread_getname_np(scheduler->scheduler_handler->native_handle(), real_name, sizeof(real_name));
    EXPECT_EQ(real_name, expect_name);

    sched_param param{};
    int policy{};
    pthread_getschedparam(scheduler->scheduler_handler->native_handle(), &policy, &param);
    EXPECT_EQ(param.sched_priority, expect_priority);
}

TEST_F(SingleThreadSchedulerUnitest, yield) {
    class ExampleTask0 : public Task {
    public:
        explicit ExampleTask0(int64_t (&p)[2]) : timestamp(p) {
            period_ms = 5U;
        }
        void period_task() override {
            timestamp[0] = get_timestamp_ms();
            task_yield();
            timestamp[1] = get_timestamp_ms();
            task_yield();
        }
    protected:
        int64_t (&timestamp)[2];
    };

    int64_t timestamp[2]{0};
    auto scheduler = std::make_unique<SingleThreadScheduler>("scheduler");
    scheduler->add_task(std::make_unique<ExampleTask0>(timestamp));
    scheduler->init();
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    scheduler = nullptr;

    EXPECT_GT(timestamp[1], timestamp[0]);
}

TEST_F(SingleThreadSchedulerUnitest, period_accuracy) {
    class ExampleTask : public Task {
    public:
        explicit ExampleTask(uint32_t period) {
            period_ms = period;
        }
        void period_task() override {
            auto current = get_timestamp_ms();
            auto real_period = current - last;
            if (counter > 1) {
                EXPECT_LE(std::abs(real_period - period_ms), period_ms * 0.1);
            }
            last = current;
            ++counter;
        }
    protected:
        int64_t last{0};
        int32_t counter{0};
    };

    auto scheduler = std::make_unique<SingleThreadScheduler>("scheduler");
    scheduler->add_task(std::make_unique<ExampleTask>(20));
    scheduler->add_task(std::make_unique<ExampleTask>(50));
    scheduler->add_task(std::make_unique<ExampleTask>(100));
    scheduler->init();
    std::this_thread::sleep_for(std::chrono::seconds(5));
    scheduler = nullptr;
}

